local AddOnName, Env = ...; local ADDON, DEV = Env[1], Env[1].DEV
-- ~~| Libraries |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local LibBoss = LibStub("LibBossIDs-1.0")
-- ~~| Lua Upvalues |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local tonumber, type 												= tonumber,type
local remove 															= table.remove
-- ~~| WoW Upvalues |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local GetTime, wipe													= GetTime, wipe
local GetInventoryItemCooldown, GetSpellCooldown			= GetInventoryItemCooldown, GetSpellCooldown
local UnitName, UnitDebuff, UnitIsVisible, UnitExists		= UnitName, UnitDebuff, UnitIsVisible, UnitExists
local UnitLevel, UnitGUID											= UnitLevel, UnitGUID
local GetSpellInfo, GetSpellLink									= GetSpellInfo, GetSpellLink
local GetSpellTabInfo, GetNumSpellTabs 						= GetSpellTabInfo, GetNumSpellTabs
local GetSpellBookItemInfo, GetSpellBookItemName			= GetSpellBookItemInfo, GetSpellBookItemName
local IsSpellInRange 												= IsSpellInRange
local RunMacroText													= RunMacroText
local IsUsableSpell													= IsUsableSpell
local GetRuneCooldown												= GetRuneCooldown
-- ~~| FireHack Upvalues |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


-- ~~| Variables |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local KEY_VALUES 	= {
  ls = 1,
  lc = 2,
  la = 4,
  rs = 8,
  rc = 16,
  ra = 32,
}
local LUST = {
	32182,	-- Heroism
	2825,		-- Bloodlust
	80353,	-- Time Warp 
	90355,	-- Ancient Hysteria
}

local previousRotation
local previousMode
local activeRotation
local activeMode
local interruptRotation
local rotationElapsed		= 0
local interruptElapsed		= 0

local spellRecast 			= {}
local spellRecastDelay 		= {}
local spellAvailableTime 	= 0.100
local gcdDelay			      = .8 -- *100
-- Spell Logging
local executedAction			= {}
local actionLog				= {}

local PreviousExecutedRotation
local previousExecutedAction

local spellBook = setmetatable({ [BOOKTYPE_SPELL] = {}, [BOOKTYPE_PET] = {}, flyout = {}},{__index = {	
	['Update'] = function(self)
		wipe(self[BOOKTYPE_SPELL])
		wipe(self[BOOKTYPE_PET])
		wipe(self.flyout)
		
		self:UpdateBook(BOOKTYPE_SPELL)
		self:UpdateBook(BOOKTYPE_PET)		
		
		for f = 1, GetNumFlyouts() do
			local flyoutID = GetFlyoutID(f)
			local _, _, numSlots, isKnown = GetFlyoutInfo(flyoutID)
			if isKnown and numSlots > 0 then
				for s = 1, numSlots do
					local spellID, _, isKnownSpell = GetFlyoutSlotInfo(flyoutID, s)
					local name = GetSpellInfo(spellID)
					if name and isKnownSpell then
						self.flyout[name] = true
					end
				end
			end
	  	end
		
	end,
	['UpdateBook'] = function(self,bookType)
		local _, _, offset, numspells = GetSpellTabInfo(GetNumSpellTabs())
		local book = self[bookType]		
		
		for bookID = 1, offset + numspells do
			local type, spellID = GetSpellBookItemInfo(bookID, bookType)
	
			if type == "SPELL" then
				local SpellBookItemName = GetSpellBookItemName(bookID, bookType)
				local baseSpellName = GetSpellInfo(spellID)
	
				if SpellBookItemName then book[SpellBookItemName] = bookID end
				if baseSpellName then book[baseSpellName] = bookID end
			end
		end	
	end,
	['GetSpellIndex'] = function(self, spell)
		local spellID = tonumber(spell)
		if spellID then spell = GetSpellInfo(spellID) end
		
		local index = self[BOOKTYPE_SPELL][spell]
		if index then return index, BOOKTYPE_SPELL end
		index = self[BOOKTYPE_PET][spell]
		if index then return index, BOOKTYPE_PET end
		index = self.flyout[spell]
		if index then return index end		
	end,	
}})

local playerTalents			= {}
-- ~~| local functions|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

local function resetRecastDelay(spellID)
	if spellID == nil or spellID == 0 or not spellRecast[spellID] then return end
	spellRecastDelay[spellID] = 0
	ADDON:Debug("rotEngine","resetRecastDelay called for spell: "..GetSpellInfo(spellID))
end
local function setRecastDelay(spellID, addCastTime)
	if spellID == nil or spellID == 0 or not spellRecast[spellID] then return end
	local castingSpellName,_, _, _, _, _, castTime = GetSpellInfo(spellID)
	if not castingSpellName then return end
	if addCastTime then
		spellRecastDelay[spellID] = (GetTime()*1000) + spellRecast[spellID] + castTime
	else
		spellRecastDelay[spellID] = (GetTime()*1000) + spellRecast[spellID]
	end
	ADDON:Debug("rotEngine","SetRecastDelay called for: "..castingSpellName)
end
-- ~~| Engine |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local function RotationEngine_OnUpdate(self, elapsed)
	if not FireHack then return end
	local rotationUpdate = self.db.rotationUpdate
	-- Rotation Engine
	rotationElapsed = rotationElapsed + elapsed
	while (rotationElapsed > rotationUpdate) do
		rotationElapsed = rotationElapsed - rotationUpdate

		if activeRotation then -- Run / start Engine		
			if ADDON.Rotations[activeRotation].rotation then ADDON.Rotations[activeRotation].rotation() end
			if activeRotation ~= previousRotation or activeMode ~= previousMode then
				if ADDON.Rotations[activeRotation].rotation then					
					ADDON.Remote:SetStatus(activeMode.."Start",{ADDON.Rotations[activeRotation].data.name,'<No Action Ready>'})
					ADDON:Debug("rotEngine","Rotation: "..activeRotation.." in mode: "..activeMode.." Enabled.")					
				end
			end
		elseif previousRotation then -- Stop Engine 
			-- reset Engine
			ADDON.Remote:SetStatus('ready')
			ADDON:Debug("rotEngine","Rotation: "..previousRotation.." in mode: "..previousMode.." Disabled.")
		end

		previousRotation 	= activeRotation
		previousMode		= activeMode
	end

	-- Interrupt Engine

end
-- ~~| Rotation Engine Methods |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local methods = {
	['StopEngine'] = function(self)
		activeRotation = nil
		activeMode		= nil
	end,
	['KeyPress'] = function(self,rotation,mode,keyDown)
		if rotation < 6 then -- rotation
			if mode == 'manual' then
				if keyDown then
					activeRotation = rotation
					activeMode		= mode
				else -- Up
					if activeRotation == rotation and activeMode == 'manual' then
						activeRotation = nil
						activeMode		= nil
					end
				end
			else -- auto
				if not activeRotation or activeRotation ~= rotation or activeMode == 'manual'  then
					activeRotation = rotation
					activeMode		= mode
				elseif activeMode == 'auto' and activeRotation == rotation then
					activeRotation = nil
					activeMode		= nil
				end
			end

		else -- Interrupt
			interruptRotation = not interruptRotation
		end
	end,
	['LogAction'] = function(self,action,spellID)

		-- clear executedActions if new rotation
		if PreviousExecutedRotation ~= activeRotation then
			wipe(executedAction)
			previousExecutedAction = nil
		end

		if previousExecutedAction == action then	-- increment execute count
			executedAction.executeCount = executedAction.executeCount  + 1
			ADDON.Remote:SetStatus('action',executedAction)
			ADDON.ActionLog:RefreshLog()
		else -- add a new action
			executedAction = {
			 	action				= action,
			 	spellID 				= spellID,
			 	author				= ADDON.Rotations[activeRotation].data.author,
				spell					= GetSpellInfo(spellID),
				executeCount		= 1,
				time					= GetTime(),
			 	mode					= activeMode,
			 	rotation				= ADDON.Rotations[activeRotation].data.name,
			}
			ADDON.Remote:SetStatus('action',executedAction)

			actionLog[#actionLog + 1] = executedAction
			if #actionLog >= 200 then remove(actionLog,1) end
			ADDON.ActionLog:RefreshLog()
		end

		previousExecutedAction		= action
		PreviousExecutedRotation	= activeRotation
	end,
	['UNIT_SPELLCAST_SENT'] = function(self, unitID, spell, rank, target, lineID)
		if unitID ~= "player" then return end

		local action = {
			action 		= 'SPELLCAST_SENT',
			spell			= spell,
			lineID		= lineID,
			time			= GetTime(),
		}
		actionLog[#actionLog + 1] = action
		if #actionLog >= 200 then remove(actionLog,1) end
		ADDON.ActionLog:RefreshLog()
	end,
	['UNIT_SPELLCAST_SUCCEEDED'] = function(self, unitID, spell, rank, lineID, spellID)	-- unitCaster, unitSpell, _, _, unitSpellID
		if unitID ~= "player" then return end
		setRecastDelay(unitSpellID)

		local action = {
			action 		= 'SPELLCAST_SUCCEEDED',
			spellID 		= spellID,
			spell			= spell,
			lineID		= lineID,
			time			= GetTime(),
		}
		actionLog[#actionLog + 1] = action
		if #actionLog >= 200 then remove(actionLog,1) end
		ADDON.ActionLog:RefreshLog()
	end,
	['UNIT_SPELLCAST_START'] = function(self,...)

	end,
	['UNIT_SPELLCAST_INTERRUPTED'] = function(self,...)
		if unitCaster ~= "player" then return end
		resetRecastDelay(unitSpellID)
	end,
	['UNIT_SPELLCAST_FAILED'] = function(self,...)

	end,
	['UNIT_SPELLCAST_FAILED_QUIET'] = function(self,...)
		self['UNIT_SPELLCAST_FAILED'](self,...)
	end,
	['SPELLS_CHANGED'] = function(self,...)
		spellBook:Update()
	end,
	['PLAYER_TALENT_UPDATE'] = function(self,...)
		wipe(playerTalents)

		for i = 1, GetMaxTalentTier() do
			local talent = select(2,GetTalentRowSelectionInfo(i))
			if talent then
				playerTalents[talent] = true
			end
		end
	end,
}
-- ~~| RotationEngine Constructor |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function ADDON:constructRotationEngine()
	local self = CreateFrame('Frame')
	self.actionLog = actionLog
	self.db = ADDON.db.profile.rotationEngine

	self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
	self:RegisterEvent("UNIT_SPELLCAST_START")
	self:RegisterEvent("UNIT_SPELLCAST_SENT")
	self:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
	self:RegisterEvent("UNIT_SPELLCAST_FAILED")
	self:RegisterEvent("UNIT_SPELLCAST_FAILED_QUIET")
	self:RegisterEvent("SPELLS_CHANGED")
	self:RegisterEvent("PLAYER_TALENT_UPDATE")
	-- self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
	-- self:RegisterEvent("UI_ERROR_MESSAGE")
	self:SetScript("OnUpdate", RotationEngine_OnUpdate)
	self:SetScript("OnEvent", function(this, event, ...) self[event](self,...) end)
	-- ~~ Methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	for method, func in pairs(methods) do
		self[method] = func
	end
	-- Trigger Events ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	-- self:PLAYER_TALENT_UPDATE()
	self:SPELLS_CHANGED()
	-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	return self
end
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- ~~| Rotation API |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

-- [[ unitID = GetUnitID(unit) 
-- @param	Unit			unit to query (string)
-- @Return	unitID		ID of unit, nil otherwise. ]]
local function GetUnitID(unit)
	if not UnitGUID(unit) then return end	
	return tonumber(UnitGUID(unit):sub(-13, -9), 16)	
end
-- [[ combat = InCombat("unit") 
-- @param	Unit			unit to query (string, UnitID)
-- @Return	combat		return true if in combat, nil otherwise ]]
local function InCombat()
	if UnitAffectingCombat("player") then return true end
	if UnitAffectingCombat("pet") then return true end	
end
local function IsChanneling(unit)
	local _, _, _, _, _, channeling = UnitChannelInfo(unit)
	if channeling and (channeling - (GetTime()*1000)) > 100 then
		return true
	end -- 100ms
	return false
end
-- [[ isBoss = IsBoss("unit") 
-- @param	Unit			unit to query (string)
-- @Return	isBoss		return true if boss, nil otherwise ]]
local function IsBoss(unit)	
	local classification = UnitClassification(unit)
  	if classification == 'rareelite' or classification == 'rare' then return true end
	if classification == 'worldboss' or LibBoss[GetUnitID(unit)] then return true end
 	return false
end 

-- TALENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- [[ check = TalentCheck(talent) 
-- @param	talent	Index of talent to check for. (number)
-- @Return	check		true if player has talent, nil otherwise. ]]
local function TalentCheck(talent)
	return(playerTalents[talent])
end

-- SPELLS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- [[ inRange = SpellInRange("spellname" or spellID, "unit") 
-- @param	spellname	Name of spell to check. (String)
-- @param	spellID		SpellID of spell to check. (Number)		
-- @param	unit 			A unit to target with the spell (String, UnitID)
-- @Return	inRange		1 if the player is near enough to cast the spell on the unit; 0 if not in range; nil if the unit is not a valid target for the spell ]]
local function SpellInRange(spell, unit)
	local index, spellBook = spellBook:GetSpellIndex(spellname)
	if spellBook then return IsSpellInRange(index, spellBook, unit) end
	
	local spellID = tonumber(spell)
	if spellID then spell = GetSpellInfo(spellID) end
	
	return IsSpellInRange(spell, unit)
end
-- [[ available = SpellAvailable(spellID) 
-- @param	spellID		SpellID of spell to check. (Number)		
-- @Return	available	true|false ]]
local function SpellAvailable(spellID)		
	local gcdStartTime, gcdDuration 		= GetSpellCooldown(61304) -- GCD
	local spellStartTime, spellDuration = GetSpellCooldown(spellID)
	if not IsUsableSpell(spellID) or not spellStartTime or not gcdDuration then return false end
	
	local spellTimeLeft = spellStartTime + spellDuration - GetTime()
	if gcdDuration == 0 then --  no GCD		
		if spellTimeLeft <= spellAvailableTime then return true end
	elseif gcdDuration > 0 then -- in GCD		
		if spellTimeLeft <= gcdDuration * (1 - gcdDelay) then return true end							
	end
	return false	
end
-- [[ time = SpellCooldownTime(spellID)
-- @param	spellID		Numeric ID of spell (number)
-- @Return	time			time left on cooldown in seconds, 0 if ready. (number)]]
local function SpellCooldownTime(spellID)	
	local start, duration, enabled = GetSpellCooldown(spellID)	
	if start ~= 0 then return ( start + duration - GetTime() ) end
	return 0
end

-- AURAS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- [[ name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId = UnitDebuffID("unit", spellID [, "filter"])
-- @param	Unit			unit to query (string)
-- @param	spellID		Numeric ID of spell (number)
-- @param	filter 		list of filters to use separated by the pipe '|' character; e.g. "CANCELABLE|PLAYER" will query cancelable debuffs cast by the player (string)
-- @Return	see WoW API UnitDebuff ]]
local function UnitDebuffID(unit, spellID, filter)	
	return UnitDebuff(unit, GetSpellInfo(spellID), nil, filter)	
end
-- [[ time = UnitDebuffTime("unit", spellID[, "filter"])
-- @param	Unit			unit to query (string)
-- @param	spellID		Numeric ID of spell (number)
-- @param	filter 		list of filters to use separated by the pipe '|' character; e.g. "CANCELABLE|PLAYER" will query cancelable debuffs cast by the player (string)
-- @Return	time			time left on debuff in seconds, 0 if debuff not present on unit. (number)]]
local function UnitDebuffTime(unit,spellID,filter)
	local _,_,_,_,_,_,debuffExpire = UnitDebuffID(unit, spellID, filter)	
	return debuffExpire and debuffExpire - GetTime() or 0
end
-- [[ stacks = UnitDebuffStacks("unit", spellID[, "filter"]) 
-- @param	Unit			unit to query (string)
-- @param	spellID		Numeric ID of spell (number)
-- @param	filter 		list of filters to use separated by the pipe '|' character; e.g. "CANCELABLE|PLAYER" will query cancelable debuffs cast by the player (string)
-- @Return	stacks		stack count of debuff, 0 if debuff not on unit. (number)]]
local function UnitDebuffStacks(unit,spellID,filter)
	local _,_,_,s = UnitDebuffID(unit, spellID, filter)
	return s and s or 0
end
-- [[ name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId = UnitBuffID("unit", spellID [, "filter"])
-- @param	Unit			unit to query (string)
-- @param	spellID		Numeric ID of spell (number)
-- @param	filter 		list of filters to use separated by the pipe '|' character; e.g. "CANCELABLE|PLAYER" will query cancelable debuffs cast by the player (string)
-- @Return	see WoW API UnitBuff ]]
local function UnitBuffID(unit, spellID, filter)	
	return UnitBuff(unit, GetSpellInfo(spellID), nil, filter)	
end
-- [[ time = UnitBuffTime("unit", spellID[, "filter"])
-- @param	Unit			unit to query (string)
-- @param	spellID		Numeric ID of spell (number)
-- @param	filter 		list of filters to use separated by the pipe '|' character; e.g. "CANCELABLE|PLAYER" will query cancelable buffs cast by the player (string)
-- @Return	time			time left on buff in seconds, 0 if buff not present on unit. (number)]]
local function UnitBuffTime(unit,spellID,filter)
	local _,_,_,_,_,_,expire = UnitBuffID(unit, spellID, filter)
	return expire and expire - GetTime() or 0
end
-- [[ stacks = UnitBuffStacks("unit", spellID[, "filter"]) 
-- @param	Unit			unit to query (string)
-- @param	spellID		Numeric ID of spell (number)
-- @param	filter 		list of filters to use separated by the pipe '|' character; e.g. "CANCELABLE|PLAYER" will query cancelable buffs cast by the player (string)
-- @Return	stacks		stack count of buff, 0 if buff not on unit. (number)]]
local function UnitBuffStacks(unit,spellID,filter)
	local _,_,_,s = UnitBuffID(unit, spellID, filter)
	return s and s or 0
end

-- DEATHKNIGHT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- [[ blood, frost, unholy, death = GetReadyRunes()
-- @Return		ready count of each rune type ]]
local function ReadyRunes()
	local blood, frost, unholy, death = 0,0,0,0
	local type
	for i=1,6 do		
		type 	=  GetRuneType(i)
		if  select(3, GetRuneCooldown(i)) then -- ready
			if type == 1 then 
				blood = blood + 1
			elseif type == 2 then 
				unholy = unholy + 1
			elseif type == 3 then 
				frost = frost + 1
			elseif type == 4 then 
				death = death + 1					
			end
		end
	end
	return blood, frost, unholy, death	
end
-- [[ depleted = GetReadyRunes()
-- @Return	depleted		number of fully delpleted runes [0-3] (number) ]]
local function DepletedRunes()
	local regenThreshold  = 1
	local depleted = 0
	for i=1,6,2 do
		local start, duration, runeReady = GetRuneCooldown(i)
		local start2, duration2, runeReady2 = GetRuneCooldown(i+1)
		if not runeReady and not runeReady2 and duration > 0 and duration2 > 0 and start > 0 and start2 > 0 then
			if (start-GetTime()+duration)>=regenThreshold and (start2-GetTime()+duration2)>=regenThreshold then
				depleted = depleted + 1
			end
		end
	end
	return depleted
end

-- ENGINE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- [[ isHotkeys = IsHotkeys(hotkeyTable)
-- @param	hotkeyTable	Table of hotkeys to check for  
-- @Return	isHotkeys	True if hotkey combination pressed or no hotkeys passed in via the hotkeyTable, nil otherwise ]]
local function IsHotkeys(hotkeyTable)
	if not hotkeyTable then return false end
	local keyTotal 		= 0
	local hotkeyTotal 	= 0
	if IsLeftShiftKeyDown()		then keyTotal = keyTotal + 1 	end
	if IsLeftControlKeyDown()	then keyTotal = keyTotal + 2 	end
	if IsLeftAltKeyDown() 		then keyTotal = keyTotal + 4 	end
	if IsRightShiftKeyDown()	then keyTotal = keyTotal + 8 	end
	if IsRightControlKeyDown()	then keyTotal = keyTotal + 16 end
	if IsRightAltKeyDown() 		then keyTotal = keyTotal + 32 end

	for h=1, #hotkeyTable do hotkeyTotal = hotkeyTotal + KEY_VALUES[hotkeyTable[h]] end
		
	return hotkeyTotal == 0 and true or hotkeyTotal == keyTotal
end
-- [[ SetGCDDelay([percent])
-- @param	percent			Percentage of GCD to wait before spamming the next action. Only applicable to actions on the GCD, if nil then set back to default (number) ]]
local function SetGCDDelay(percent)
	if type(percent) ~= 'number' then 
		gcdDelay = .8-- reset
	return end
	gcdDelay = percent / 100	
end
-- [[ success = CastAction("actionName" [, spellID, "unit", recastDelay, cancelChannel])
-- @param	actionName		Short descriptive name for your action (string)
-- @param	spellID			Numeric ID of spell you want to cast (number)
-- @param	Unit				target for your spell (string, UnitID)
-- @param	recastDelay		Set a delay for your spell, triggered on a succesful cast (number)
-- @param	cancelChannel	allow this action to interupt a channeled spell (Boolean)
-- @Return	success			True if spell met casting requirements, false otherwise. ]]
local function CastAction(actionName, spellID, unit, recastDelay, cancelChannel)	
	if not spellID then ADDON:Debug("rotEngine",actionName.." has no spellID, relying on rotation conditional lua.") return true end
	
	local spellIndex, spellBook = spellBook:GetSpellIndex(spellID)
	local spellname = GetSpellInfo(spellID)
	if not spellIndex then return false	end
	local mouse
	
	if unit and unit:lower() == 'mouse' then unit = nil mouse = true end 
	if unit and ( not UnitExists(unit) or not UnitIsVisible(unit) ) then return false end	
	if spellBook then
		if spellBook == BOOKTYPE_SPELL then
			if unit and SpellHasRange(spellIndex, spellBook) == 1 then			
				if IsSpellInRange(spellIndex, spellBook, unit) == 0 then return false end
				if IsHarmfulSpell(spellIndex, spellBook) and not UnitCanAttack("player", unit) then return false end			
			end
		elseif spellBook == BOOKTYPE_PET then 
			if not UnitExists('pet') or IsChanneling('pet') then return false end		
		end
	else
		spellID = spellIndex
		spellname = GetSpellInfo(spellID)
		if unit and SpellHasRange(spellname) == 1 then
			if IsSpellInRange(spellname, unit) == 0 then return false end
			if IsHarmfulSpell(spellname) and not UnitCanAttack("player", unit) then return false end			
		end
	end
	
	if IsChanneling('player') and not cancelChannel then return false end
	if not SpellAvailable(spellID) then return false end	
	if spellRecastDelay[spellID] and spellRecastDelay[spellID] >= (GetTime() * 1000)	then return false end
	
	-- We can attempt to cast the spell	
	spellRecast[spellID] = recastDelay or 0
	
	ADDON.RotationEngine:LogAction(actionName,spellID)
	
	if unit then 
		ADDON:Debug("rotEngine","Casting "..actionName.." ("..spellname..") on "..UnitName(unit))		
		CastSpellByName(spellname, unit)
	else
		if mouse then			
			ADDON:Debug("rotEngine","Casting "..actionName.." ("..spellname..") on mouse")
			CameraOrSelectOrMoveStart(1)
         CastSpellByName(spellname)
         CameraOrSelectOrMoveStop(1) 	
		else	
			ADDON:Debug("rotEngine","Casting "..actionName.." ("..spellname..")  ")		
			CastSpellByName(spellname)
		end
	end
	
	return true	
	-- spellId == 116694 spellId == 124682 spellId == 123273
end

ADDON.API = {
	InCombat				= InCombat,
	SetGCDDelay			= SetGCDDelay,
	TalentCheck			= TalentCheck,
	SpellInRange 		= SpellInRange,
	SpellAvailable 	= SpellAvailable,
	SpellCooldownTime	= SpellCooldownTime,
	IsHotkeys			= IsHotkeys,
	IsBoss 				= IsBoss,
	IsChanneling 		= IsChanneling,
	CastAction 			= CastAction,
	UnitBuffID 			= UnitBuffID,
	UnitDebuffID 		= UnitDebuffID,
	UnitBuffTime 		= UnitBuffTime,
	UnitDebuffTime 	= UnitDebuffTime,	
	UnitBuffStacks 	= UnitBuffStacks,
	UnitDebuffStacks 	= UnitDebuffStacks,	
	ReadyRunes 			= ReadyRunes,
	DepletedRunes 		= DepletedRunes,
	GetUnitID			= GetUnitID,
}



